package com.dynamic.mybatis.core.parser.xml.validator;

import org.apache.ibatis.builder.BuilderException;
import org.w3c.dom.Document;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.stream.StreamSource;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.StringReader;

public class MybatisXmlValidator {
    static String headSchemaLanguage = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
            + "<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/" + DmsXMLMapperEntityResolver.DmsSchema.dms_dtd.systemId + "\">\n";
    static String headMapper = "<mapper namespace=\"%s\">\n";
    static String footMapper = "</mapper>";


    public static String buildXmlMapperBody(String xmlMapperBody, String namespace) {
        return buildXmlMapper(String.format(headMapper, namespace) + xmlMapperBody + footMapper);
    }

    public static String buildXmlMapper(String xmlMapper) {
        return headSchemaLanguage + xmlMapper;
    }


    public static StreamSource streamSource(String xml) {
        return new StreamSource(new StringReader(xml));
    }

    public static InputStream inputStream(String xml) {
        return new ByteArrayInputStream(xml.getBytes());
    }


    public static void validateXmlMapperBody(String xmlMapperBody, String namespace) throws SAXParseException {
        String xml = buildXmlMapperBody(xmlMapperBody, namespace);
        validate(new InputSource(new ByteArrayInputStream(xml.getBytes())), 3);
    }

    public static void validate(String xml) throws SAXParseException {
        validate(new InputSource(new ByteArrayInputStream(xml.getBytes())));
    }

    public static void validate(String xml, int subLineNumber) throws SAXParseException {
        validate(new InputSource(new ByteArrayInputStream(xml.getBytes())), subLineNumber);
    }

    private static Document validate(InputSource inputSource) throws SAXParseException {
        return validate(inputSource, 0);
    }

    private static Document validate(InputSource inputSource, int subLineNumber) throws SAXParseException {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
            //factory.setFeature(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI,true);
            factory.setValidating(true);
            factory.setNamespaceAware(false);
            factory.setIgnoringComments(true);
            factory.setIgnoringElementContentWhitespace(false);
            factory.setCoalescing(false);
            factory.setExpandEntityReferences(true);

            DocumentBuilder builder = factory.newDocumentBuilder();
            builder.setEntityResolver(new DmsXMLMapperEntityResolver());
            builder.setErrorHandler(new ErrorHandler() {
                @Override
                public void error(SAXParseException exception) throws SAXException {
                    throw exception;
                }

                @Override
                public void fatalError(SAXParseException exception) throws SAXException {
                    throw exception;
                }

                @Override
                public void warning(SAXParseException exception) throws SAXException {
                    exception.printStackTrace();
                    // NOP
                }
            });
            Document document = builder.parse(inputSource);
            validateAttribute(document);
            return document;
        } catch (Exception e) {
            if (e instanceof SAXParseException) {
                if (subLineNumber > 0) {
                    SAXParseException saxParseException = (SAXParseException) e;
                    throw new SAXParseException(null, null, null, saxParseException.getLineNumber() - subLineNumber, saxParseException.getColumnNumber(), e);
                }
                throw (SAXParseException) e;
            }
            throw new BuilderException("Error creating document instance.  Cause: " + e, e);
        }
    }

    //TODO 属性必填校验
    private static void validateAttribute(Document document) {

    }

    public static void main(String[] args) throws SAXParseException {
        String xmlMapperBody = "<select id=\"a\" resultType=\"map\">\n"
                + "  SELECT * FROM users WHERE users.name = #{name}\n"
                + "  <where>\n"
                + "\t<if  test=\"username != null and username !=''\">\n"
                + "\t  and username = #{username}\n"
                + "\t</if>\n"
                + "  </where>\n"
                + "</select>\n";
        MybatisXmlValidator.validateXmlMapperBody(xmlMapperBody, "test.Mapper");
    }


}
